Popular Searches
Popular Course Categories
Popular Courses

Creating an Expense Tracker with Charts and Categories

Creating an Expense Tracker with Charts and Categories

Real-World iOS Application Projects

Creating an Expense Tracker with Charts and Categories

Course: iOS App Development

Topic: Creating an Expense Tracker with Charts and Categories

Course Link: iOS App Development Course

Demo Link: Register for a Course Demo

Website: JustAcademy


1. Introduction to an Expense Tracker Application

An Expense Tracker is an iOS application that helps users record, organize, monitor, and analyze their daily spending. It allows users to enter expenses, assign categories, view transaction histories, and understand their financial habits through charts and summaries.

Building an Expense Tracker is a practical iOS development project because it combines several important technologies, including Swift, SwiftUI, data modeling, local persistence, forms, navigation, filtering, and data visualization.

Main Features of an Expense Tracker

  • Add, edit, and delete expense records.
  • Organize expenses into categories such as Food, Travel, Shopping, and Bills.
  • Display total spending for a selected period.
  • Show category-wise spending through charts.
  • Display daily, weekly, and monthly expense trends.
  • Filter transactions by category and date.
  • Search expenses using descriptions or notes.
  • Store records locally so they remain available after restarting the app.
  • Display spending summaries and recent transactions.

2. Technologies Used to Build the Application

A modern Expense Tracker can be developed using Apple's native frameworks. SwiftUI provides the user interface, SwiftData or Core Data can manage local persistence, and Swift Charts can visualize financial information.

Technology Purpose
Swift Implements application logic, calculations, models, and business rules.
SwiftUI Builds screens, forms, lists, buttons, navigation, and dashboard layouts.
SwiftData Provides model-based local data persistence for supported deployment targets.
Core Data Alternative framework for managing persistent structured application data.
Swift Charts Creates bar charts, line charts, and category distribution charts.
Foundation Provides dates, formatting, collections, and other standard utilities.
Xcode Provides the development, testing, debugging, and build environment.

Recommended Architecture

A simple project can use a model-driven SwiftUI structure. Separate models, reusable views, and calculation logic so that the application remains easy to maintain as features grow.

ExpenseTracker/
|
|-- Models/
|   |-- Expense.swift
|   |-- ExpenseCategory.swift
|
|-- Views/
|   |-- DashboardView.swift
|   |-- AddExpenseView.swift
|   |-- ExpenseListView.swift
|   |-- CategoryView.swift
|   |-- ChartsView.swift
|
|-- Services/
|   |-- ExpenseCalculator.swift
|   |-- CurrencyFormatter.swift
|
|-- Utilities/
|   |-- DateFilter.swift
|
|-- ExpenseTrackerApp.swift

3. Planning the Expense Tracker User Interface

Before writing code, identify the screens and the information each screen should display. A clear screen structure makes the application easier to use and simplifies implementation.

Important Application Screens

Screen Responsibilities
Dashboard Displays total spending, summaries, category charts, and recent transactions.
Add Expense Allows users to enter an amount, description, date, and category.
Expense History Displays saved expense records in a list.
Categories Displays available expense categories and their totals.
Analytics Shows spending patterns through charts and date filters.
Settings Provides preferences such as currency and data management options.

Example Navigation Structure

Expense Tracker
      |
      |-- Dashboard
      |     |-- Total Expenses
      |     |-- Category Chart
      |     |-- Monthly Trend
      |
      |-- Transactions
      |     |-- Expense Details
      |     |-- Edit Expense
      |
      |-- Add Expense
      |
      |-- Categories
      |
      |-- Settings

4. Designing the Expense Data Model

The expense model represents one financial transaction. Each record should contain enough information to calculate totals, display transaction details, and group expenses into categories.

Important Expense Properties

Property Data Type Purpose
id UUID Uniquely identifies an expense.
title String Describes the transaction.
amount Decimal Stores the expense amount accurately.
date Date Records when the expense occurred.
category String or relationship Identifies the expense category.
notes String Stores additional transaction details.
paymentMethod String or enum Records cash, card, UPI, or another payment method.

Example Expense Model Using SwiftData

import Foundation
import SwiftData

@Model
final class Expense {
    var id: UUID
    var title: String
    var amount: Decimal
    var date: Date
    var category: String
    var notes: String
    var paymentMethod: String

    init(
        title: String,
        amount: Decimal,
        date: Date = Date(),
        category: String,
        notes: String = "",
        paymentMethod: String = "Cash"
    ) {
        self.id = UUID()
        self.title = title
        self.amount = amount
        self.date = date
        self.category = category
        self.notes = notes
        self.paymentMethod = paymentMethod
    }
}

Why Use Decimal for Money?

Financial calculations should avoid unnecessary binary floating-point rounding errors. Swift's Decimal type is useful for representing monetary amounts and performing financial calculations.

For applications requiring strict financial accuracy, also define a consistent currency policy, rounding rules, and storage strategy. Do not assume every currency uses two decimal places.

5. Creating Expense Categories

Categories organize transactions into meaningful groups. They help users understand where their money is going and make category-wise analysis possible.

Common Expense Categories

Category Example Expenses Suggested SF Symbol
Food Restaurants, groceries, snacks. fork.knife
Travel Taxi, bus, train, fuel. car
Shopping Clothing, electronics, accessories. bag
Bills Electricity, internet, mobile bills. doc.text
Entertainment Movies, games, subscriptions. film
Health Medicines, consultations, fitness. heart
Education Books, courses, learning materials. book
Other Miscellaneous transactions. ellipsis.circle

Creating a Category Enum

For a simple application with fixed categories, an enum provides a convenient way to define category values.

import Foundation

enum ExpenseCategory: String, CaseIterable, Identifiable {
    case food = "Food"
    case travel = "Travel"
    case shopping = "Shopping"
    case bills = "Bills"
    case entertainment = "Entertainment"
    case health = "Health"
    case education = "Education"
    case other = "Other"

    var id: String { rawValue }
}

Benefits of Using an Enum

  • Prevents inconsistent category spelling.
  • Provides a fixed set of category choices.
  • Makes Picker controls easier to implement.
  • Supports predictable grouping and filtering.
  • Reduces errors caused by arbitrary category text.

For applications that allow users to create, rename, recolor, or delete categories, use a persistent category model instead of relying only on a fixed enum.

6. Building the Add Expense Form

The Add Expense screen allows users to record a transaction. A well-designed form should validate the amount, provide category selection, and make date entry straightforward.

Form Fields

  • Expense title or description.
  • Amount.
  • Category selection.
  • Transaction date.
  • Payment method.
  • Optional notes.

Example Add Expense View

import SwiftUI

struct AddExpenseView: View {
    @Environment(\.dismiss) private var dismiss
    @Environment(\.modelContext) private var context

    @State private var title = ""
    @State private var amountText = ""
    @State private var category = ExpenseCategory.food.rawValue
    @State private var date = Date()
    @State private var paymentMethod = "Cash"
    @State private var notes = ""

    var body: some View {
        NavigationStack {
            Form {
                Section("Expense Details") {
                    TextField("Expense title", text: $title)

                    TextField("Amount", text: $amountText)
                        .keyboardType(.decimalPad)

                    Picker("Category", selection: $category) {
                        ForEach(ExpenseCategory.allCases) { item in
                            Text(item.rawValue)
                                .tag(item.rawValue)
                        }
                    }

                    DatePicker(
                        "Date",
                        selection: $date,
                        displayedComponents: .date
                    )
                }

                Section("Payment") {
                    Picker("Payment Method", selection: $paymentMethod) {
                        Text("Cash").tag("Cash")
                        Text("Card").tag("Card")
                        Text("UPI").tag("UPI")
                        Text("Other").tag("Other")
                    }

                    TextField("Notes (Optional)", text: $notes)
                }

                Button("Save Expense") {
                    saveExpense()
                }
                .disabled(!isValid)
            }
            .navigationTitle("Add Expense")
            .toolbar {
                ToolbarItem(placement: .topBarTrailing) {
                    Button("Cancel") {
                        dismiss()
                    }
                }
            }
        }
    }

    private var isValid: Bool {
        guard let amount = Decimal(string: amountText) else {
            return false
        }

        return !title.trimmingCharacters(
            in: .whitespacesAndNewlines
        ).isEmpty && amount > 0
    }

    private func saveExpense() {
        guard let amount = Decimal(string: amountText),
              isValid else {
            return
        }

        let expense = Expense(
            title: title,
            amount: amount,
            date: date,
            category: category,
            notes: notes,
            paymentMethod: paymentMethod
        )

        context.insert(expense)
        dismiss()
    }
}

Form Validation Rules

  • Expense title should not be empty.
  • Amount must be a valid positive number.
  • Category must be selected.
  • Date should be valid and appropriate for the application's rules.
  • Optional notes should not prevent saving.
  • Invalid input should produce clear feedback.

7. Saving Expenses with SwiftData

SwiftData provides a model-based approach to storing application data. It integrates with SwiftUI and supports querying model objects, inserting records, editing records, and deleting records.

Configure the Model Container

import SwiftUI
import SwiftData

@main
struct ExpenseTrackerApp: App {
    var body: some Scene {
        WindowGroup {
            DashboardView()
        }
        .modelContainer(for: Expense.self)
    }
}

Important SwiftData Components

Component Purpose
@Model Marks a class as a persistent model.
modelContainer Configures the persistent storage container for the application.
@Query Fetches model records and updates SwiftUI views when relevant data changes.
modelContext Provides access to insert, delete, and manage model objects.
context.insert() Adds a new model object to the persistence context.
context.delete() Marks a model object for deletion.

Fetching Expenses

import SwiftUI
import SwiftData

struct ExpenseListView: View {
    @Query(sort: \Expense.date, order: .reverse)
    private var expenses: [Expense]

    var body: some View {
        List(expenses) { expense in
            VStack(alignment: .leading) {
                Text(expense.title)
                    .font(.headline)

                Text(expense.category)
                    .font(.subheadline)

                Text(expense.amount.description)
            }
        }
        .navigationTitle("Expenses")
    }
}

SwiftData manages persistence through its model context. For production applications, define appropriate model relationships, migration strategies, validation rules, and error-handling behavior.

8. Displaying Expenses in a List

The transaction list provides users with a chronological view of their spending. Each row should show the transaction title, category, date, and formatted amount.

Example Expense Row

struct ExpenseRowView: View {
    let expense: Expense

    var body: some View {
        HStack(spacing: 12) {
            Image(systemName: "creditcard")
                .font(.title2)
                .frame(width: 42, height: 42)
                .background(
                    Color.blue.opacity(0.12)
                )
                .clipShape(RoundedRectangle(cornerRadius: 10))

            VStack(alignment: .leading, spacing: 4) {
                Text(expense.title)
                    .font(.headline)

                Text(expense.category)
                    .font(.caption)
                    .foregroundStyle(.secondary)

                Text(expense.date, style: .date)
                    .font(.caption2)
                    .foregroundStyle(.secondary)
            }

            Spacer()

            Text(expense.amount.formatted())
                .font(.headline)
        }
        .padding(.vertical, 5)
    }
}

List Features to Implement

  • Sort records by newest or oldest date.
  • Search using expense titles and notes.
  • Filter by category.
  • Display formatted currency values.
  • Allow users to open transaction details.
  • Provide edit and delete actions.
  • Show a helpful empty state when there are no expenses.

9. Editing and Deleting Expenses

Users should be able to correct transaction details and remove records they no longer need. Editing should update the existing model rather than creating an unnecessary duplicate.

Editing an Expense

  1. Allow the user to select an expense from the list.
  2. Open an edit form populated with the existing values.
  3. Allow the user to modify the title, amount, date, category, and notes.
  4. Validate the updated information.
  5. Save changes to the existing model.
  6. Refresh the dashboard and charts using the updated data.

Deleting an Expense

import SwiftUI
import SwiftData

struct ExpenseManagementView: View {
    @Environment(\.modelContext) private var context

    @Query private var expenses: [Expense]

    var body: some View {
        List {
            ForEach(expenses) { expense in
                ExpenseRowView(expense: expense)
            }
            .onDelete(perform: deleteExpenses)
        }
    }

    private func deleteExpenses(at offsets: IndexSet) {
        for index in offsets {
            context.delete(expenses[index])
        }
    }
}

In a production app, consider confirming destructive actions and ensuring that deleting an expense updates all summaries and charts immediately.

10. Calculating Total Expenses

The dashboard should calculate total spending for the selected date range. Calculations should use the same filtering rules as the transaction list and charts so that all screens display consistent information.

Basic Total Calculation

func totalExpenses(_ expenses: [Expense]) -> Decimal {
    expenses.reduce(Decimal.zero) {
        $0 + $1.amount
    }
}

Calculating Monthly Expenses

func monthlyExpenses(
    from expenses: [Expense],
    month: Date,
    calendar: Calendar = .current
) -> Decimal {
    guard let interval = calendar.dateInterval(
        of: .month,
        for: month
    ) else {
        return .zero
    }

    return expenses
        .filter {
            $0.date >= interval.start &&
            $0.date < interval.end
        }
        .reduce(Decimal.zero) {
            $0 + $1.amount
        }
}

Useful Dashboard Metrics

  • Total expenses for the current month.
  • Total expenses for the previous month.
  • Average daily spending.
  • Highest spending category.
  • Number of recorded transactions.
  • Recent expense activity.

11. Grouping Expenses by Category

Category grouping combines individual transactions into meaningful totals. For example, if a user records three food expenses of ₹200, ₹350, and ₹150, the total Food category spending is ₹700.

Category Grouping Logic

func categoryTotals(
    from expenses: [Expense]
) -> [String: Decimal] {
    Dictionary(grouping: expenses, by: \.category)
        .mapValues { items in
            items.reduce(Decimal.zero) {
                $0 + $1.amount
            }
        }
}

Example Category Summary

Category Expenses Total
Food 3 ₹700
Travel 2 ₹500
Shopping 1 ₹1,200
Bills 2 ₹1,000
Total 8 ₹3,400

12. Introduction to Swift Charts

Swift Charts is Apple's framework for building data visualizations in SwiftUI. It provides chart components such as BarMark, LineMark, PointMark, and SectorMark. Developers can combine these components to display trends, comparisons, and category distributions.

For an Expense Tracker, Swift Charts can visualize monthly spending, daily expenses, and the proportion of total expenses assigned to each category.

Common Chart Types

Chart Type Purpose Expense Tracker Example
Bar Chart Compares values across groups. Compare total expenses by category.
Line Chart Displays changes over time. Show daily or monthly spending trends.
Donut Chart Displays parts of a whole. Show category contribution to total spending.
Area Chart Highlights changes and overall magnitude over time. Show cumulative spending trends.

Importing Swift Charts

import SwiftUI
import Charts

13. Creating a Category Spending Bar Chart

A bar chart makes it easy to compare how much money users spend in different categories. Each bar represents a category, and its height represents the total expense amount.

Define Chart Data

struct CategoryExpense: Identifiable {
    let id = UUID()
    let category: String
    let total: Double
}

Example Bar Chart

import SwiftUI
import Charts

struct CategoryBarChartView: View {
    let data: [CategoryExpense]

    var body: some View {
        Chart(data) { item in
            BarMark(
                x: .value("Category", item.category),
                y: .value("Amount", item.total)
            )
            .foregroundStyle(by: .value(
                "Category",
                item.category
            ))
            .cornerRadius(6)
        }
        .frame(height: 250)
        .chartYAxis {
            AxisMarks(position: .leading)
        }
        .chartLegend(position: .bottom)
    }
}

Advantages of Bar Charts

  • Compare category spending quickly.
  • Identify the highest spending category.
  • Highlight differences between expense groups.
  • Support category-level financial decisions.

14. Creating a Donut Chart for Category Distribution

A donut chart shows how the total expense amount is divided among categories. It is useful when users want to understand the relative contribution of Food, Travel, Shopping, Bills, and other categories.

Donut Chart Implementation

import SwiftUI
import Charts

struct CategoryDonutChartView: View {
    let data: [CategoryExpense]

    var body: some View {
        Chart(data) { item in
            SectorMark(
                angle: .value("Amount", item.total),
                innerRadius: .ratio(0.58),
                angularInset: 2
            )
            .foregroundStyle(by: .value(
                "Category",
                item.category
            ))
        }
        .frame(height: 280)
        .chartLegend(position: .bottom)
    }
}

Understanding SectorMark

Property Purpose
angle Controls the size of a sector based on its value.
innerRadius Creates the central hole in a donut chart.
angularInset Controls spacing between sectors.
foregroundStyle Provides visual category differentiation.

15. Creating a Monthly Expense Trend Line Chart

A line chart shows how spending changes over time. It can help users identify days or months when expenses increase or decrease.

Define Monthly Data

struct MonthlyExpense: Identifiable {
    let id = UUID()
    let month: Date
    let total: Double
}

Line Chart Implementation

import SwiftUI
import Charts

struct MonthlyTrendChartView: View {
    let data: [MonthlyExpense]

    var body: some View {
        Chart(data) { item in
            LineMark(
                x: .value("Month", item.month),
                y: .value("Expense", item.total)
            )
            .interpolationMethod(.catmullRom)

            PointMark(
                x: .value("Month", item.month),
                y: .value("Expense", item.total)
            )
        }
        .frame(height: 260)
        .chartXAxis {
            AxisMarks(values: .automatic(desiredCount: 6)) {
                AxisValueLabel(format: .dateTime.month(.abbreviated))
            }
        }
    }
}

Where Line Charts Are Useful

  • Comparing monthly spending.
  • Monitoring daily expenses.
  • Identifying spending spikes.
  • Reviewing long-term financial habits.
  • Comparing current and previous periods.

16. Preparing Data for Charts

Charts should use aggregated data rather than displaying every raw transaction as a separate category total. Before creating a chart, filter the expense records and group them according to the required reporting period or category.

Convert Decimal Totals for Chart Display

func chartData(
    from expenses: [Expense]
) -> [CategoryExpense] {
    let totals = categoryTotals(from: expenses)

    return totals.map { category, amount in
        CategoryExpense(
            category: category,
            total: NSDecimalNumber(
                decimal: amount
            ).doubleValue
        )
    }
    .sorted {
        $0.total > $1.total
    }
}

Use Decimal for financial calculations and convert to Double only at the chart presentation boundary when needed by the chart's numeric values. Avoid using chart display values as the source of financial calculations.

Chart Data Preparation Steps

  1. Fetch expense records from persistent storage.
  2. Apply the selected date range.
  3. Apply any selected category filters.
  4. Group the filtered records.
  5. Calculate totals using Decimal.
  6. Convert values for visualization when required.
  7. Sort the results for meaningful presentation.
  8. Display the resulting chart.

17. Building a Dashboard Screen

The dashboard is the main screen of the Expense Tracker. It combines financial summaries, category charts, monthly trends, and recent transaction records.

Recommended Dashboard Components

  • Total expense summary card.
  • Selected reporting period.
  • Category spending chart.
  • Monthly trend chart.
  • Top spending category.
  • Recent transaction list.
  • Add Expense button.

Example Dashboard Structure

import SwiftUI
import SwiftData

struct DashboardView: View {
    @Query(sort: \Expense.date, order: .reverse)
    private var expenses: [Expense]

    var body: some View {
        NavigationStack {
            ScrollView {
                VStack(spacing: 20) {
                    Text("Expense Dashboard")
                        .font(.largeTitle.bold())

                    Text("Total Spending")
                        .font(.headline)

                    Text(totalExpenses(expenses).formatted())
                        .font(.title.bold())

                    Text("Category Breakdown")
                        .font(.headline)

                    CategoryDonutChartView(
                        data: chartData(from: expenses)
                    )

                    Text("Recent Transactions")
                        .font(.headline)

                    ForEach(expenses.prefix(5)) { expense in
                        ExpenseRowView(expense: expense)
                    }
                }
                .padding()
            }
            .navigationTitle("Overview")
        }
    }
}

18. Filtering Expenses by Date

Date filtering allows users to review expenses over specific periods. Common options include Today, This Week, This Month, Previous Month, and Custom Range.

Common Date Filters

Filter Purpose
Today Displays expenses recorded on the current calendar day.
This Week Displays expenses within the current calendar week.
This Month Displays expenses within the current calendar month.
Last Month Displays expenses from the previous calendar month.
Last 30 Days Displays expenses within a rolling 30-day period.
Custom Range Displays expenses between user-selected dates.

Filtering by Month

func expensesForMonth(
    _ expenses: [Expense],
    date: Date,
    calendar: Calendar = .current
) -> [Expense] {
    guard let interval = calendar.dateInterval(
        of: .month,
        for: date
    ) else {
        return []
    }

    return expenses.filter {
        $0.date >= interval.start &&
        $0.date < interval.end
    }
}

Why Use Calendar APIs?

  • They account for calendar boundaries.
  • They support different locales and calendar settings.
  • They avoid fragile manual date comparisons.
  • They make week and month filtering easier to maintain.

19. Filtering Expenses by Category

Category filtering helps users focus on one type of spending. For example, a user may want to see only Travel expenses to understand transportation costs.

Category Filter Example

func expenses(
    _ expenses: [Expense],
    in category: String
) -> [Expense] {
    expenses.filter {
        $0.category == category
    }
}

Combining Date and Category Filters

func filteredExpenses(
    _ expenses: [Expense],
    category: String?,
    startDate: Date,
    endDate: Date
) -> [Expense] {
    expenses.filter { expense in
        let matchesCategory =
            category == nil ||
            expense.category == category

        let matchesDate =
            expense.date >= startDate &&
            expense.date < endDate

        return matchesCategory && matchesDate
    }
}

When filtering by date, define whether the end date is inclusive or exclusive. Using an exclusive end boundary often makes date-range calculations more predictable.

20. Searching Expense Records

Search allows users to locate transactions quickly. The application can search titles, notes, category names, or other suitable text fields.

Example Search Logic

func searchExpenses(
    _ expenses: [Expense],
    query: String
) -> [Expense] {
    let searchText = query
        .trimmingCharacters(in: .whitespacesAndNewlines)

    guard !searchText.isEmpty else {
        return expenses
    }

    return expenses.filter {
        $0.title.localizedCaseInsensitiveContains(searchText) ||
        $0.category.localizedCaseInsensitiveContains(searchText) ||
        $0.notes.localizedCaseInsensitiveContains(searchText)
    }
}

21. Displaying Currency and Financial Values

Currency formatting ensures that amounts are displayed in a familiar format. A financial application should consider the user's currency, locale, grouping separators, decimal precision, and currency symbol.

Currency Formatting Example

import Foundation

func currencyText(
    amount: Decimal,
    locale: Locale = .current
) -> String {
    amount.formatted(
        .currency(code: "INR")
            .locale(locale)
    )
}

let amount: Decimal = 1250.50
print(currencyText(amount: amount))

Currency Formatting Best Practices

  • Use an explicit currency code when the application supports a defined currency.
  • Use locale-aware formatting for grouping and decimal separators.
  • Do not manually concatenate currency symbols with numbers.
  • Do not assume all currencies use two decimal places.
  • Keep stored monetary values separate from their display formatting.
  • Define how the application handles currency conversion if multiple currencies are supported.

22. Adding Category Colors and Icons

Colors and icons make categories easier to recognize. Each category can have a consistent visual identity that appears in transaction rows, category selectors, and charts.

Example Category Icon Mapping

func categoryIcon(_ category: String) -> String {
    switch category {
    case "Food":
        return "fork.knife"
    case "Travel":
        return "car"
    case "Shopping":
        return "bag"
    case "Bills":
        return "doc.text"
    case "Entertainment":
        return "film"
    case "Health":
        return "heart"
    case "Education":
        return "book"
    default:
        return "ellipsis.circle"
    }
}

Visual Design Guidelines

  • Use the same icon for a category throughout the application.
  • Choose colors with sufficient contrast.
  • Do not rely on color alone to communicate category information.
  • Include readable category labels in chart legends.
  • Use SF Symbols that clearly represent the category.

23. Creating Reusable Summary Cards

Summary cards display important financial metrics in a compact format. Reusable components make it easier to maintain a consistent dashboard design.

Example Summary Card

struct SummaryCardView: View {
    let title: String
    let amount: Decimal
    let symbol: String

    var body: some View {
        VStack(alignment: .leading, spacing: 10) {
            Label(title, systemImage: symbol)
                .font(.subheadline)
                .foregroundStyle(.secondary)

            Text(amount.formatted(.currency(code: "INR")))
                .font(.title2.bold())
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding()
        .background(.thinMaterial)
        .clipShape(RoundedRectangle(cornerRadius: 16))
    }
}

24. Handling Empty States and No Chart Data

When an application has no expense records, charts may have no values to display. The dashboard should explain the situation and guide the user toward adding the first transaction.

Empty State Examples

  • No expenses recorded: "Add your first expense to start tracking spending."
  • No category results: "There are no expenses in this category."
  • No monthly data: "No transactions found for this month."
  • No search results: "Try another search term."

Example Empty State View

ContentUnavailableView(
    "No Expenses Yet",
    systemImage: "chart.bar",
    description: Text(
        "Add your first expense to see your spending summary."
    )
)

25. Improving Chart Accessibility

Charts should be understandable to users who rely on assistive technologies. Swift Charts supports accessibility features, and developers should provide meaningful descriptions and ensure that important financial information is also available in text form.

Accessibility Best Practices

  • Provide descriptive chart titles.
  • Use clear axis labels and category names.
  • Provide VoiceOver-friendly descriptions.
  • Do not communicate differences using color alone.
  • Ensure chart values are available in accessible text summaries.
  • Use readable text sizes and sufficient contrast.
  • Test the application with VoiceOver.

26. Testing the Expense Tracker

Testing ensures that financial calculations, data persistence, filtering, and charts behave correctly. Since the application handles monetary values, calculation accuracy is particularly important.

Functional Testing

  • Add an expense with a valid amount.
  • Attempt to save an empty expense title.
  • Enter an invalid or negative amount.
  • Edit an existing expense.
  • Delete an expense.
  • Restart the application and verify saved records.
  • Filter expenses by category.
  • Filter expenses by date range.
  • Verify category totals.
  • Verify monthly totals.
  • Check that charts update after records are edited or deleted.

Example Calculation Test Cases

Test Case Input Expected Result
Single expense ₹500 ₹500 total
Multiple expenses ₹200 + ₹300 ₹500 total
Category total Food: ₹200 + ₹150 ₹350 Food total
Empty records No expenses Zero total and empty state
Deleted transaction Remove ₹100 expense Total decreases by ₹100

27. Performance and Data Management Best Practices

  • Use efficient queries when the transaction history becomes large.
  • Aggregate chart data instead of repeatedly calculating totals in every view.
  • Keep financial calculation logic separate from UI components.
  • Use appropriate persistence models and relationships.
  • Avoid unnecessary chart recomputation.
  • Use stable identifiers for categories and expenses.
  • Handle data migration when the model structure changes.
  • Consider backup and export options for important user records.
  • Protect sensitive financial information.
  • Test performance with realistic transaction volumes.

28. Common Problems and Troubleshooting

Problem Possible Cause Solution
Expense does not save Model context is unavailable or validation fails. Check model container setup, context access, and validation logic.
Expense disappears after restart Persistence is not configured correctly. Verify SwiftData model container and persistent storage configuration.
Category total is incorrect Incorrect grouping or filtering logic. Review category keys and calculation boundaries.
Chart is empty No chart data or incorrect aggregation. Inspect the filtered data before creating chart marks.
Chart does not update View is not observing the updated data or chart input is stale. Use reactive data fetching and derive chart data from current records.
Currency appears incorrectly Incorrect locale or currency code. Use explicit currency formatting and verify locale settings.
Date filter misses records Incorrect start or end date boundaries. Use Calendar date intervals and consistent inclusive/exclusive rules.
Duplicate expenses appear New records are inserted when existing records should be updated. Update the existing model object during editing.
Chart labels overlap Too many categories or long labels. Use shorter labels, horizontal charts, or category filtering.

29. Mini Project: Complete Expense Tracker Application

Project Objective: Build a functional iOS Expense Tracker that allows users to record transactions, organize spending by category, and understand their financial habits through charts.

Project Requirements

  • Create a SwiftUI application with a dashboard.
  • Design an Expense data model.
  • Implement local persistence using SwiftData.
  • Create default expense categories.
  • Build an Add Expense form.
  • Implement editing and deleting functionality.
  • Display recent transactions.
  • Calculate total spending.
  • Group expenses by category.
  • Create a category bar chart.
  • Create a category donut chart.
  • Create a monthly spending trend chart.
  • Implement date and category filters.
  • Add currency formatting.
  • Provide empty states and input validation.
  • Test the application on an iOS simulator and physical device.

Expected Application Output

At the end of the project, the application should provide a dashboard with total expenses, category-wise spending, monthly trends, and recent transactions. Users should be able to add, edit, delete, search, and filter expense records.

30. Interview Questions and Answers

Q1. What is an Expense Tracker application?

Answer: An Expense Tracker is an application that records financial transactions, organizes them into categories, calculates spending totals, and provides financial summaries.

Q2. Why is SwiftUI suitable for building an Expense Tracker?

Answer: SwiftUI provides declarative UI components such as forms, lists, navigation, and reusable views, making it suitable for building a modern financial dashboard.

Q3. What is SwiftData?

Answer: SwiftData is Apple's model-based persistence framework that allows developers to define models and manage persistent application data.

Q4. What is the purpose of the @Model macro?

Answer: The @Model macro makes a class a SwiftData persistent model so its instances can be stored and managed by a model container.

Q5. Why should categories be used in an Expense Tracker?

Answer: Categories group similar transactions and help users understand how much they spend on food, travel, shopping, bills, and other activities.

Q6. What is Swift Charts?

Answer: Swift Charts is Apple's framework for creating data visualizations such as bar charts, line charts, and sector charts in SwiftUI.

Q7. What is the purpose of BarMark?

Answer: BarMark represents values using rectangular bars and is useful for comparing expense totals across categories.

Q8. What is SectorMark?

Answer: SectorMark creates a chart sector and is useful for displaying category contributions in pie or donut charts.

Q9. Why is Decimal useful for monetary values?

Answer: Decimal is designed for decimal arithmetic and helps avoid common binary floating-point precision issues in financial calculations.

Q10. How can monthly expenses be calculated?

Answer: Filter expenses within the selected month's Calendar date interval and add their monetary amounts.

Q11. How can category-wise totals be calculated?

Answer: Group expense records by category and calculate the sum of each group's amounts.

Q12. Why should chart data be aggregated?

Answer: Aggregation converts individual transactions into meaningful summaries, making charts easier to read and more efficient to display.

Q13. How can an application support date filtering?

Answer: Use Calendar APIs to calculate the required date interval and filter records using consistent start and end boundaries.

Q14. How do you handle empty chart data?

Answer: Display an empty state with a clear message and guide the user to add transactions or change the selected filter.

Q15. How can an Expense Tracker maintain accurate totals?

Answer: Use Decimal for financial calculations, centralize calculation logic, validate input, and ensure that filters and charts use consistent data.

31. Quick Revision Checklist

  • Understand the Expense Tracker application workflow.
  • Design the Expense data model.
  • Understand the role of expense categories.
  • Create forms using SwiftUI.
  • Validate expense input.
  • Store records using SwiftData.
  • Fetch and display transactions.
  • Implement edit and delete functionality.
  • Calculate total and monthly expenses.
  • Group expenses by category.
  • Create charts using Swift Charts.
  • Use BarMark for category comparisons.
  • Use SectorMark for category distribution.
  • Use LineMark for spending trends.
  • Implement date and category filters.
  • Format monetary values correctly.
  • Test calculations, persistence, and chart updates.

32. Conclusion

Creating an Expense Tracker with Charts and Categories is an excellent practical project for learning iOS application development. It combines SwiftUI interface design, Swift data modeling, local persistence, transaction management, category grouping, and financial calculations.

By integrating Swift Charts, developers can transform transaction records into meaningful visual summaries. Category charts help users understand where their money goes, while monthly trend charts reveal changes in spending habits over time.

A well-designed Expense Tracker should prioritize accurate financial calculations, reliable data storage, clear navigation, accessible visualizations, and a smooth user experience. Completing this project gives learners practical experience with several important technologies used in modern iOS applications.

33. Official Documentation and Learning Resources

Previous Next
whatsapp